﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using OPCAutomation;
using OPCDADemo.Model;

namespace OPCDADemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private OPCServer m_opc = null;
        private OPCGroups m_opcGrps;
        private OPCGroup m_opcGrp = null;
        private int m_updateRate = 1000;
        private OPCItems m_opcItems;
        private int m_nClientCount = 1;
        private DataTable m_dataTable;
        private List<string> m_listTempOpcItemName;
        private List<TreeNode> m_listTreeNode;

        private CSVFileHelper m_cSVFileHelper;
        private WindowGroup m_windowGroup = null;
        private List<string> m_strlistPrt;
        private int m_nPrtInfolines = 0;
        private string m_strServerName;

        MainWindowModel m_model;
        public MainWindow()
        {
            InitializeComponent();
            // 部分成员变量初始化
            m_listTempOpcItemName = new List<string>();
            m_listTreeNode = new List<TreeNode>();
            m_model = new MainWindowModel();


            Binding bindingStr2Prt = new Binding("StrPrtInfo") { Source = m_model};
            BindingOperations.SetBinding(prtTxtBrowser, TextBox.TextProperty, bindingStr2Prt);
            // 这边就像QT的信号与槽一样，在初始化的时候就把banding连接好，当然项目大了之后也可以把这部分代码分离出来，但一般来讲问题不大
            // banding中指定好数据源，然后再setBinding中将数据源与目标绑定
            Binding bindingTxt2BoxMan = new Binding("StrTxtBoxMan") {Source = m_model};
            BindingOperations.SetBinding(txtBoxMan, TextBox.TextProperty, bindingTxt2BoxMan);
            m_model.StrTxtBoxMan = "Channel1.Device1.test2";

            Binding bindingTxt2GrpName = new Binding("StrGrpName") { Source = m_model};
            BindingOperations.SetBinding(txtGrpName, TextBox.TextProperty, bindingTxt2GrpName);
            m_model.StrGrpName = "DefaultGrp1";

            Binding bindingTxt2WriteValue = new Binding("StrWriteValue") { Source = m_model };
            BindingOperations.SetBinding(txtBoxWriteValue, TextBox.TextProperty, bindingTxt2WriteValue);
            m_model.StrWriteValue = "333";
            

        }
        /// <summary>
        /// 打印信息至主界面textBox
        /// </summary>
        /// <param name="prtInfo"></param>
        private void Print2TxtBox(string prtInfo)
        {
            m_nPrtInfolines++;
            int len = m_model.StrPrtInfo.Length;
            m_model.StrPrtInfo += string.Format("{0}:", m_nPrtInfolines) + prtInfo;
           
            if (m_model.StrPrtInfo.Length > 1000)
            {
                m_model.StrPrtInfo.Remove(0, prtInfo.Length);
            }
        }
        /// <summary>
        /// 连接服务器按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                m_opc = new OPCServer();
                // Connect(string ProgID, object Node)
                // ProgID, Program ID，用于标识服务器的，一般就是服务器名
                // Node，节点，实际上就是用于连接远程的IP，不填就是本地
                string serverName;
                if (radioBtnV4.IsChecked != null && radioBtnV4.IsChecked != false)
                {
                    serverName = radioBtnV4.Content.ToString();
                }
                else 
                {
                    serverName = radioBtnV6.Content.ToString();
                }
                m_opc.Connect(serverName);
                if (m_opc.ServerState == (int)OPCServerState.OPCRunning)
                {
                    Print2TxtBox(serverName + "连接成功\n");
                    TreeNode treeNode = new TreeNode{ParentID = 0, NodeID = 1, NodeName = serverName };
                    m_listTreeNode.Add(treeNode);
                }
                else
                {
                    Print2TxtBox(m_opc.ServerState.ToString());
                }
            }
            catch(Exception err)
            {
                Print2TxtBox("连接服务器出现错误:" + err.Message);
                return ;
            }
        }

        /// <summary>
        /// 添加组按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddGroup_Click(object sender, RoutedEventArgs e)
        {
            if (m_opc != null && m_opc.ServerState == (int)OPCServerState.OPCRunning)
            {
                m_opcGrps = m_opc.OPCGroups;
            }
            else
            {
                // AppendText不是同步的/需要函数返回才处理
                Print2TxtBox("OPCServer未正确连接!\n");
                return;
            }
            // GrpName必须是唯一的，若填空，则系统生成唯一标识；若填已有标识，则报错
            try
            { 
                m_opcGrp = m_opcGrps.Add(m_model.StrGrpName);
                if (m_opcGrp != null)
                {
                    Print2TxtBox(String.Format("组:{0} 添加成功\n", m_model.StrGrpName));
                }

            }
            catch (Exception)
            {
                Print2TxtBox(String.Format("组:{0} 添加失败\n", m_model.StrGrpName));
                return;
            }
            SetGroupProperty();
            // 当组内项数据变化事件发生时，产生opcGroup_DataChange委托
            m_opcGrp.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(opcGroup_DataChange);
            m_opcItems = m_opcGrp.OPCItems;
            // serverHandle 是服务器分配给组的，它唯一标识着组
            // handle-句柄，这个概念是windows系统编程特有的，它是对象具有的一个索引，我们通过句柄来操作整个对象，正如其直译-操纵杆/把手
            Print2TxtBox(string.Format("ServerHandle=0x{0:X}\n", m_opcGrp.ServerHandle));
        }

        private void opcGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
        {
            for (int i = 1; i <= NumItems; i++)
            {
                /*if ((Convert.ToInt32(ClientHandles.GetValue(i)) == 1))
                {
                    prtTxtBrowser.AppendText(ItemValues.GetValue(i).ToString() + "\n");
                }*/
                Print2TxtBox(ItemValues.GetValue(i).ToString() + "\n");
            }

        }
        private void SetGroupProperty()
        {
            m_opc.OPCGroups.DefaultGroupIsActive = true;
            m_opc.OPCGroups.DefaultGroupDeadband = 0;
            m_opcGrp.UpdateRate = m_updateRate;
            m_opcGrp.IsActive = true;
            m_opcGrp.IsSubscribed = true;
        }
        /// <summary>
        /// 添加项（变量）
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddItems_Click(object sender, RoutedEventArgs e)
        {

            /* warning: opc的数组操作都是从1开始的!!! 
                这将导致：
            1. 你的所有参数存放都必须从1开始
            2. 数组长度必须+1
            3. 特别是不同容器转换时，有的容器默认是0开始的，这时需要手动转换
             */
            if (m_opcGrp != null)
            {
                // ClientHandle是客户端处的一个标识，似乎是自定义的
                // int ClientHandle = m_nClientCount++;
                if (radioBtnMan.IsChecked == true)
                {
                    m_listTempOpcItemName.Add(txtBoxMan.Text);
                }
                // else 已经从CSV导入到m_listTempOpcItemName中了

                try
                {
                    // AddItems()的参数准备和类型转换
                    int nAddItemCount = m_listTempOpcItemName.Count();
                    int [] nClientHandles = new int[nAddItemCount + 1];
                    
                    Array nServerHandles = new int[nAddItemCount + 1];
                    object requestedDataType;   // 可选参数，可不填
                    object accessPaths;         // 可选参数，可不填
                    Array errors;
                    Array arrayTempOpcItemName = new string[nAddItemCount + 1];
                    m_listTempOpcItemName.ToArray().CopyTo(arrayTempOpcItemName, 1);
                    for (int i = 1; i <= nAddItemCount; i++)
                    {
                        nClientHandles[i] = m_nClientCount + i + 1;
                    }
                    Array arrayClientHandles; 
                    arrayClientHandles = nClientHandles.ToArray();
                    m_opcItems.AddItems(nAddItemCount, ref arrayTempOpcItemName,
                        ref arrayClientHandles, out nServerHandles, out errors/*, requestedDataType, accessPaths*/);
                    for (int i = 1; i <= errors.Length; i++)
                    {
                        Print2TxtBox(string.Format("错误:{0}\n", errors.GetValue(i)));
                    }
                    for (int i = 1; i <= nServerHandles.Length; i++)
                    {
                        Print2TxtBox(string.Format("服务器句柄:{0}\n", nServerHandles.GetValue(i)));
                    }
                    for (int i = 0; i < nAddItemCount; i++)
                    {
                        Print2TxtBox(string.Format("项:{0}添加成功\n", m_listTempOpcItemName[i]));
                    }
                }
                catch (Exception ex)
                {
                    Print2TxtBox(string.Format("添加项 {0} 异常\n", ex.Message));
                }
            }
            else
            {
                Print2TxtBox(string.Format("对应组不存在!\n"));
                return;
            }
            
        }
        /// <summary>
        /// 写入OpcServer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnWriteValue_Click(object sender, RoutedEventArgs e)
        {
            // 首先writeValue是Group的方法，但是写入是指向Item的
            // void AsyncWrite(int NumItems, ref Array ServerHandles,
            // ref Array Values, out Array Errors, int TransactionID, out int CancelID);
            // 参数：
            // 1. NumItems，需要写的Item的数量
            // 2. ServerHandles，写入的各个Item的ServerHandle，WriteValue是Grp的方法，有可能会误以为是Grp的
            // Notes: 几个Array参数都是从数据下标1开始的，而不是0
            // ServerHandle，其实这边是每个Item的ServerHandle，Item的ServerHandle也是由服务器分配的/唯一的
            Array errors;
            int cancelID;
            Array values = new object[2] {"", txtBoxWriteValue.Text};
            Array handles = new int[2] { 0, m_opcItems.Item(1).ServerHandle };
            //handles.SetValue(m_opcGrp.ServerHandle, 0);
            m_opcGrp.AsyncWrite(1, ref handles, ref values, out errors, 1, out cancelID);
            // void SyncWrite(int NumItems, ref Array ServerHandles, ref Array Values, out Array Errors);
            //m_opcGrp.SyncWrite();
        }
        /// <summary>
        /// 主动读取变量值
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReadValue_Click(object sender, RoutedEventArgs e)
        {
            Array errors;
            int cancelID;
            Array values = new Object[2];
            Array handles = new int[2] { 0, m_opcItems.Item(1).ServerHandle };
            m_opcGrp.AsyncRead(1, ref handles, out errors, 1, out cancelID);
            // prtTxtBrowser.AppendText();
        }
        /// <summary>
        /// csv导入按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnImportCSV_Click(object sender, RoutedEventArgs e)
        {
            m_cSVFileHelper = new CSVFileHelper();
            var openFileDialog = new Microsoft.Win32.OpenFileDialog()
            {
                Filter = "csv文件(*.csv)|*.csv"
            };
            var result = openFileDialog.ShowDialog();
            if (result == true)
            {
                m_dataTable = CSVFileHelper.ReadCSV(openFileDialog.FileName);
                foreach (DataRow dr in m_dataTable.Rows)
                {
                    m_listTempOpcItemName.Add(string.Format("{0}.{1}.{2}", dr[0], dr[1], dr[2]));
                    /*
                    for (int j = 0; j < m_dataTable.Columns.Count; j++)
                        
                    {
                        prtTxtBrowser.AppendText(dr[j].ToString() + " | ");
                        
                    }
                    prtTxtBrowser.AppendText("\n");
                   */
                }
                foreach (string str in m_listTempOpcItemName)
                {
                    Print2TxtBox(string.Format("检索到{0}\n", str));
                }
            }
        }
        /// <summary>
        /// 查看已经添加的组
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCheckGroup_Click(object sender, RoutedEventArgs e)
        {
            m_windowGroup = new WindowGroup();
            // prtTxtBrowser.AppendText(windowGroup.PersistId.ToString());
            m_windowGroup.InputData(m_listTreeNode);
            
            m_windowGroup.ShowDialog();

        }
    }
}
